home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / KJBUCKETS0.PY < prev    next >
Encoding:
Python Source  |  1999-07-28  |  29.2 KB  |  1,050 lines

  1.  
  2. # kjbuckets in pure python
  3.  
  4. ### needs more thorough testing!
  5.  
  6. #import sys # for debug
  7.  
  8. def kjtabletest(x):
  9.     #print "kjtabletest"
  10.     try:
  11.         return x.is_kjtable
  12.     except:
  13.         return 0
  14.         
  15. unhashable = "unhashable key error"
  16.  
  17. class kjGraph:
  18.  
  19.    is_kjtable = 1
  20.  
  21.    def __init__(self, *args):
  22.        #print "kjGraph.__init__", args
  23.        key_to_list = self.key_to_list = {}
  24.        self.dirty = 0
  25.        self.hashed = None
  26.        #print args
  27.        if args:
  28.           if len(args)>1:
  29.              raise ValueError, "only 1 or 0 argument supported"
  30.           from types import IntType, ListType, TupleType
  31.           arg = args[0]
  32.           targ = type(arg)
  33.           test = key_to_list.has_key
  34.           if type(arg) is IntType:
  35.              return # ignore int initializer (presize not implemented)
  36.           elif type(arg) is ListType or type(arg) is TupleType:
  37.              for (x,y) in arg:
  38.                  if test(x):
  39.                     key_to_list[x].append(y)
  40.                  else:
  41.                     key_to_list[x] = [y]
  42.              return
  43.           aclass = arg.__class__
  44.           if aclass is kjGraph:
  45.              aktl = arg.key_to_list
  46.              for k in aktl.keys():
  47.                  key_to_list[k] = aktl[k][:]
  48.              return
  49.           if aclass is kjDict or aclass is kjSet:
  50.              adict = arg.dict
  51.              for k in adict.keys():
  52.                  key_to_list[k] = [ adict[k] ]
  53.              return
  54.           raise ValueError, "arg for kjGraph must be tuple, list, or kjTable"
  55.  
  56.    def __repr__(self):
  57.        return "%s(%s)" % (self.__class__.__name__, self.items())
  58.  
  59.    def _setitems(self, thing):
  60.        #print "kjGraph._setitem", thing
  61.        #print "setitems", thing
  62.        if self.hashed is not None: 
  63.           raise ValueError, "table has been hashed, it is immutable"
  64.        try:
  65.            for (k,v) in thing:
  66.                #print k,v, "going"
  67.                #inlined __setitem__
  68.                try:
  69.                    klist = self.key_to_list[k]
  70.                    #print "klist gotten"
  71.                except KeyError:
  72.                    try:
  73.                        klist = self.key_to_list[k] = []
  74.                    except TypeError:
  75.                        raise unhashable
  76.                if v not in klist:
  77.                    klist.append(v)
  78.        except (TypeError, KeyError):
  79.           #print sys.exc_type, sys.exc_value
  80.           if kjtabletest(thing):
  81.              self._setitems(thing._pairs())
  82.              self.dirty = thing.dirty
  83.           else: raise ValueError, "cannot setitems with %s" % type(thing)
  84.        except unhashable:
  85.           raise TypeError, "unhashable type"
  86.  
  87.    def __setitem__(self, item, value):
  88.        ktl = self.key_to_list
  89.        if ktl.has_key(item):
  90.           l = ktl[item]
  91.           if value not in l:
  92.              l.append(value)
  93.        else:
  94.           ktl[item] = [value]
  95.  
  96.    def __getitem__(self, item):
  97.        return self.key_to_list[item][0]
  98.  
  99.    def __delitem__(self, item):
  100.        self.dirty = 1
  101.        del self.key_to_list[item]
  102.  
  103.    def choose_key(self):
  104.        return self.key_to_list.keys()[0]
  105.  
  106.    def _pairs(self, justtot=0):
  107.        myitems = self.key_to_list.items()
  108.        tot = 0
  109.        for (k, v) in myitems:
  110.            tot = tot + len(v)
  111.        if justtot: return tot
  112.        else:
  113.           result = [None]*tot
  114.           i = 0
  115.           for (k,v) in myitems:
  116.               for x in v:
  117.                   result[i] = (k,x)
  118.                   i = i+1
  119.        return result
  120.  
  121.    def __len__(self):
  122.        v = self.key_to_list.values()
  123.        lv = map(len, v)
  124.        from operator import add
  125.        return reduce(add, lv, 0)
  126.  
  127.    def items(self):
  128.        return self._pairs()
  129.  
  130.    def values(self):
  131.        v = self.key_to_list.values()
  132.        from operator import add
  133.        tot = reduce(add, map(len, v), 0)
  134.        result = [None] * tot
  135.        count = 0
  136.        for l in v:
  137.            next = count + len(l)
  138.            result[count:next] = l
  139.            count = next
  140.        return result
  141.  
  142.    def keys(self):
  143.        return self.key_to_list.keys()
  144.  
  145.    def member(self, k, v):
  146.        ktl = self.key_to_list
  147.        if ktl.has_key(k):
  148.           return v in ktl[k]
  149.        return 0
  150.  
  151.    _member = member # because member redefined for kjSet
  152.  
  153.    def add(self, k, v):
  154.        ktl = self.key_to_list
  155.        if ktl.has_key(k):
  156.           l = ktl[k]
  157.           if v not in l:
  158.              l.append(v)
  159.        else:
  160.           ktl[k] = [v]
  161.  
  162.    def delete_arc(self, k, v):
  163.        self.dirty = 1
  164.        if self.hashed is not None: 
  165.           raise ValueError, "table has been hashed, it is  immutable"
  166.        try:
  167.            l = self.key_to_list[k]
  168.            i = l.index(v)
  169.            del l[i]
  170.            if not l:
  171.               del self.key_to_list[k]
  172.        except:
  173.            raise KeyError, "not in table"# % (k,v)
  174.  
  175.    def has_key(self, k):
  176.        return self.key_to_list.has_key(k)
  177.  
  178.    def subset(self, other):
  179.        oc = other.__class__
  180.        if oc is kjGraph:
  181.           oktl = other.key_to_list
  182.           sktl = self.key_to_list
  183.           otest = oktl.has_key
  184.           for k in sktl.keys():
  185.               if otest(k):
  186.                  l = sktl[k]
  187.                  ol = oktl[k]
  188.                  for x in l:
  189.                      if x not in ol:
  190.                         return 0
  191.               else:
  192.                  return 0
  193.           return 1
  194.        elif oc is kjSet or oc is kjDict:
  195.           sktl = self.key_to_list
  196.           odict = other.dict
  197.           otest = odict.has_key
  198.           for k in sktl.keys():
  199.               if otest(k):
  200.                  l = sktl[k]
  201.                  ov = odict[k]
  202.                  for x in l:
  203.                      if ov!=x: return 0
  204.               else:
  205.                  return 0
  206.           return 1
  207.  
  208.    def neighbors(self, k):
  209.        try:
  210.            return self.key_to_list[k][:]
  211.        except:
  212.            return []
  213.  
  214.    def reachable(self, k):
  215.        try:
  216.            horizon = self.key_to_list[k]
  217.        except:
  218.            return kjSet()
  219.        else:
  220.            if not horizon: return []
  221.            d = {}
  222.            for x in horizon: d[x] = 1
  223.            done = 0
  224.            while horizon:
  225.               newhorizon = []
  226.               for n in horizon:
  227.                   for n2 in self.neighbors(n):
  228.                       if not d.has_key(n2):
  229.                          newhorizon.append(n2)
  230.                          d[n2] = 1
  231.               horizon = newhorizon
  232.            return kjSet(d.keys())
  233.  
  234.    def items(self):
  235.        return self._pairs()
  236.  
  237.  
  238.    # ????
  239.    def ident(self):
  240.        result = kjDict(self)
  241.        result.dirty = self.dirty or result.dirty
  242.        return result
  243.  
  244.    def tclosure(self):
  245.        # quick and dirty
  246.        try:
  247.            raise self
  248.        except (kjSet, kjDict):
  249.            raise ValueError, "tclosure only defined on graphs"
  250.        except kjGraph:
  251.            pass
  252.        except:
  253.            raise ValueError, "tclosure only defined on graphs"
  254.        result = kjGraph(self)
  255.        result.dirty = self.dirty
  256.        addit = result.add
  257.        while 1:
  258.           #print result
  259.           more = result*result
  260.           if more.subset(result):
  261.              return result
  262.           for (x,y) in more.items():
  263.               addit(x,y)
  264.  
  265.    def Clean(self):
  266.        if self.dirty: return None
  267.        return self
  268.  
  269.    def Wash(self):
  270.        self.dirty = 0
  271.  
  272.    def Soil(self):
  273.        self.dirty = 1
  274.  
  275.    def remap(self, X):
  276.        # really only should be defined for kjdict, but whatever
  277.        return kjDict(X*self).Clean()
  278.  
  279.    def dump(self, seq):
  280.        result = map(None, seq)
  281.        for i in range(len(result)):
  282.            result[i] = self[result[i]]
  283.        if len(seq) == 1:
  284.           return result[0]
  285.        return tuple(result)
  286.  
  287.    def __hash__(self): # should test better
  288.        """in conformance with kjbuckets, permit unhashable keys"""
  289.        if self.hashed is not None:
  290.           return self.hashed
  291.        items = self._pairs()
  292.        for i in xrange(len(items)):
  293.            (a,b) = items[i]
  294.            try:
  295.               b = hash(b)
  296.            except:
  297.               b = 1877777
  298.            items[i] = hash(a)^~b
  299.        items.sort()
  300.        result = self.hashed = hash(tuple(items))
  301.        return result
  302.  
  303.    def __cmp__(self, other):
  304.        #print "kjGraph.__cmp__"
  305.        ls = len(self)
  306.        lo = len(other)
  307.        test = cmp(ls, lo)
  308.        if test:
  309.           return test
  310.        si = self._pairs()
  311.        oi = other._pairs()
  312.        si.sort()
  313.        oi.sort()
  314.        return cmp(si, oi)
  315.        
  316.    def __nonzero__(self):
  317.        if self.key_to_list: return 1
  318.        return 0
  319.  
  320.    def __add__(self, other):
  321.        result = kjGraph(self)
  322.        rktl = result.key_to_list
  323.        rtest = rktl.has_key
  324.        result.dirty = self.dirty or other.dirty
  325.        oc = other.__class__
  326.        if oc is kjGraph:
  327.           oktl = other.key_to_list
  328.           for k in oktl.keys():
  329.               l = oktl[k]
  330.               if rtest(k):
  331.                  rl = rktl[k]
  332.                  for x in l:
  333.                      if x not in rl:
  334.                         rl.append(x)
  335.               else:
  336.                  rktl[k] = l[:]
  337.        elif oc is kjSet or oc is kjDict:
  338.           odict = other.dict
  339.           for k in odict.keys():
  340.               ov = odict[k]
  341.               if rtest(k):
  342.                  rl = rktl[k]
  343.                  if ov not in rl:
  344.                     rl.append(ov)
  345.               else:
  346.                  rktl[k] = [ov]
  347.        else:
  348.           raise ValueError, "kjGraph adds only with kjTable"
  349.        return result
  350.  
  351.    __or__ = __add__
  352.  
  353.    def __sub__(self, other):
  354.        result = kjGraph()
  355.        rktl = result.key_to_list
  356.        sktl = self.key_to_list
  357.        oc = other.__class__
  358.        if oc is kjGraph:
  359.           oktl = other.key_to_list
  360.           otest = oktl.has_key
  361.           for k in sktl.keys():
  362.               l = sktl[k][:]
  363.               if otest(k):
  364.                  ol = oktl[k]
  365.                  for x in ol:
  366.                      if x in l:
  367.                         l.remove(x)
  368.                  if l: 
  369.                     rktl[k] = l
  370.               else:
  371.                  rktl[k] = l
  372.        elif oc is kjSet or oc is kjDict:
  373.            odict = other.dict
  374.            otest = odict.has_key
  375.            for k in sktl.keys():
  376.                l = sktl[k][:]
  377.                if otest(k):
  378.                   ov = odict[k]
  379.                   if ov in l:
  380.                      l.remove(ov)
  381.                if l:
  382.                   rktl[k] = l
  383.        else:
  384.           raise ValueError, "kjGraph diffs only with kjTable"
  385.        return result
  386.  
  387.    def __mul__(self, other):
  388.        result = kjGraph()
  389.        rktl = result.key_to_list
  390.        sktl = self.key_to_list
  391.        oc = other.__class__
  392.        if oc is kjGraph:
  393.           oktl = other.key_to_list
  394.           otest = other.has_key
  395.           for sk in sktl.keys():
  396.               sklist = []
  397.               for sv in sktl[sk]:
  398.                   if otest(sv):
  399.                      sklist[0:0] = oktl[sv]
  400.               if sklist:
  401.                  rktl[sk] = sklist
  402.        elif oc is kjSet or oc is kjDict:
  403.           odict = other.dict
  404.           otest = odict.has_key
  405.           for sk in sktl.keys():
  406.               sklist=[]
  407.               for sv in sktl[sk]:
  408.                   if otest(sv):
  409.                      sklist.append(odict[sv])
  410.               if sklist:
  411.                  rktl[sk] = sklist
  412.        else:
  413.           raise ValueError, "kjGraph composes only with kjTable"
  414.        return result
  415.  
  416.    def __invert__(self):
  417.        result = self.__class__()
  418.        pairs = self._pairs()
  419.        for i in xrange(len(pairs)):
  420.            (k,v) = pairs[i]
  421.            pairs[i] = (v,k)
  422.        result._setitems(pairs)
  423.        result.dirty = self.dirty or result.dirty
  424.        return result
  425.  
  426.    def __and__(self, other):
  427.        sktl = self.key_to_list
  428.        oc = other.__class__
  429.        if oc is kjGraph:
  430.           result = kjGraph()
  431.           rktl = result.key_to_list
  432.           oktl = other.key_to_list
  433.           otest = oktl.has_key
  434.           for k in self.keys():
  435.               if otest(k):
  436.                  l = sktl[k]
  437.                  ol = oktl[k]
  438.                  rl = []
  439.                  for x in l:
  440.                      if x in ol:
  441.                         rl.append(x)
  442.                  if rl:
  443.                     rktl[k] = rl
  444.        elif oc is kjSet or oc is kjDict:
  445.           result = oc() # less general!
  446.           rdict = result.dict
  447.           odict = other.dict
  448.           stest = sktl.has_key
  449.           for k in odict.keys():
  450.               if stest(k):
  451.                  v = odict[k]
  452.                  l = sktl[k]
  453.                  if v in l:
  454.                     rdict[k] = v
  455.        else:
  456.           raise ValueError, "kjGraph intersects only with kjTable"
  457.        result.dirty = self.dirty or other.dirty
  458.        return result
  459.  
  460.    def __coerce__(self, other):
  461.        return (self, other) # ?is this sufficient?
  462.  
  463. class kjDict(kjGraph):
  464.  
  465.    def __init__(self, *args):
  466.        #print "kjDict.__init__", args
  467.        self.hashed = None
  468.        dict = self.dict = {}
  469.        self.dirty = 0
  470.        if not args: return
  471.        if len(args)==1:
  472.           from types import TupleType, ListType, IntType
  473.           arg0 = args[0]
  474.           targ0 = type(arg0)
  475.           if targ0 is IntType: return
  476.           if targ0 is ListType or targ0 is TupleType:
  477.              otest = dict.has_key
  478.              for (a,b) in arg0:
  479.                  if otest(a):
  480.                     if dict[a]!=b:
  481.                        self.dirty = 1
  482.                  dict[a] = b
  483.              return
  484.           argc = arg0.__class__
  485.           if argc is kjGraph:
  486.              ktl = arg0.key_to_list
  487.              for k in ktl.keys():
  488.                  l = ktl[k]
  489.                  if len(l)>1: self.dirty=1
  490.                  for v in l:
  491.                      dict[k] = v
  492.              return
  493.           if argc is kjSet or argc is kjDict:
  494.              adict = arg0.dict
  495.              for (k,v) in adict.items():
  496.                  dict[k]=v
  497.              return
  498.        raise ValueError, "kjDict initializes only from list, tuple, kjTable, or int"
  499.  
  500.    def _setitems(self, thing):
  501.        #print "kjDict._setitem", thing
  502.        if self.hashed is not None: 
  503.           raise KeyError, "table hashed, cannot modify"
  504.        dict = self.dict
  505.        try:
  506.           for (k,v) in thing:
  507.               if dict.has_key(k) and dict[k]!=v:
  508.                  self.dirty = 1
  509.               dict[k] = v
  510.        except:
  511.           self._setitems(thing._pairs()) # maybe too tricky!
  512.           
  513.    def dump(self, dumper):
  514.        ld = len(dumper)
  515.        if ld==1:
  516.           return self.dict[dumper[0]]
  517.        else:
  518.           sdict = self.dict
  519.           result = [None] * ld
  520.           for i in xrange(ld):
  521.               result[i] = sdict[ dumper[i] ]
  522.           return tuple(result)
  523.           
  524.    def __setitem__(self, item, value):
  525.        if self.hashed is not None:
  526.           raise ValueError, "table has been hashed, it is immutable"
  527.        d = self.dict
  528.        if d.has_key(item):
  529.           if d[item]!=value:
  530.              self.dirty = 1
  531.        self.dict[item]=value
  532.  
  533.    def __getitem__(self, item):
  534.        return self.dict[item]
  535.  
  536.    def __delitem__(self, item):
  537.        if self.hashed is not None:
  538.           raise ValueError, "table has been hashed, it is immutable"
  539.        self.dirty = 1
  540.        del self.dict[item]
  541.  
  542.    def choose_key(self):
  543.        return self.dict.keys()[0]
  544.        
  545.    def __len__(self):
  546.        return len(self.dict)    
  547.  
  548.    def _pairs(self, justtot=0):
  549.        if justtot: return len(self.dict)
  550.        return self.dict.items()
  551.  
  552.    def values(self):
  553.        return self.dict.values()
  554.  
  555.    def keys(self):
  556.        return self.dict.keys()
  557.        
  558.    def items(self):
  559.        return self.dict.items()
  560.        
  561.    def remap(self, X):
  562.        if X.__class__ is kjGraph:
  563.           if self.dirty or X.dirty: return None
  564.           result = kjDict()
  565.           resultd = result.dict
  566.           selfd = self.dict
  567.           inself = selfd.has_key
  568.           inresult = resultd.has_key
  569.           ktl = X.key_to_list
  570.           for k in ktl.keys():
  571.               for v in ktl[k]:
  572.                   if inself(v):
  573.                      map = selfd[v]
  574.                      if inresult(k):
  575.                         if resultd[k]!=map:
  576.                            return None
  577.                      else:
  578.                         resultd[k]=map
  579.           return result
  580.        else:
  581.           return (kjDict(X*self)).Clean()
  582.           
  583.    def __cmp__(s,o):
  584.        from types import InstanceType
  585.        if type(o) is not InstanceType:
  586.           return -1
  587.        oc = o.__class__
  588.        if oc is kjDict or oc is kjSet:
  589.           return cmp(s.dict, o.dict)
  590.        return kjGraph.__cmp__(s, o)
  591.        
  592.    def __hash__(s):
  593.        h = s.hashed
  594.        if h is not None: return h
  595.        return kjGraph.__hash__(s)
  596.        
  597.    def __add__(s,o):
  598.        oc = o.__class__
  599.        if oc is kjDict or oc is kjSet:
  600.           result = kjDict()
  601.           result.dirty = s.dirty or o.dirty
  602.           rdict = result.dict
  603.           rtest = result.has_key
  604.           sdict = s.dict
  605.           for k in sdict.keys():
  606.               rdict[k] = sdict[k]
  607.           odict = o.dict
  608.           for k in odict.keys():
  609.               if rtest(k):
  610.                  if rdict[k]!=odict[k]:
  611.                     result.dirty=1
  612.               else:
  613.                  rdict[k] = odict[k]
  614.           return result
  615.        if oc is kjGraph:
  616.           return kjGraph.__add__(o,s)
  617.        else:
  618.           raise ValueError, "kjDict unions only with kjTable"
  619.        
  620.    __or__ = __add__
  621.        
  622.    def __and__(s,o):
  623.        oc = o.__class__
  624.        if oc is kjDict or oc is kjSet:
  625.           result = oc()
  626.           result.dirty = s.dirty or o.dirty
  627.           rdict = result.dict
  628.           odict = o.dict
  629.           sdict = s.dict
  630.           stest = sdict.has_key
  631.           for k in odict.keys():
  632.               v = odict[k]
  633.               if stest(k) and sdict[k]==v:
  634.                  rdict[k] = v
  635.           return result
  636.        elif oc is kjGraph:
  637.           return kjGraph.__and__(o,s)
  638.           
  639.  
  640.    def __sub__(s,o):
  641.        oc = o.__class__
  642.        result = kjDict()
  643.        result.dirty = s.dirty or o.dirty
  644.        sdict = s.dict
  645.        rdict = result.dict
  646.        if oc is kjDict:
  647.           odict = o.dict
  648.           otest = odict.has_key
  649.           for k in sdict.keys():
  650.               v = sdict[k]
  651.               if otest(k):
  652.                  if odict[k]!=v:
  653.                     rdict[k] = v
  654.               else:
  655.                  rdict[k] = v
  656.           return result
  657.        if oc is kjGraph:
  658.           oktl = o.key_to_list
  659.           otest = oktl.has_key
  660.           for k in sdict.keys():
  661.               v = sdict[k]
  662.               if otest(k):
  663.                  if v not in oktl[k]:
  664.                     rdict[k] = v
  665.               else:
  666.                  rdict[k] = v
  667.           return result
  668.        raise ValueError, "kjDict only diffs with kjGraph, kjDict"
  669.           
  670.    def __mul__(s,o):
  671.        oc = o.__class__
  672.        sdict = s.dict
  673.        if oc is kjDict or oc is kjSet:
  674.           result = kjDict()
  675.           result.dirty = s.dirty or o.dirty
  676.           rdict = result.dict
  677.           odict = o.dict
  678.           otest = odict.has_key
  679.           for k in sdict.keys():
  680.               kv = sdict[k]
  681.               if otest(kv):
  682.                  rdict[k] = odict[kv]
  683.           return result
  684.        elif oc is kjGraph:
  685.           return kjGraph(s) * o
  686.        else:
  687.           raise ValueError, "kjDict only composes with kjTable"
  688.  
  689.    def member(self, k, v):
  690.        d = self.dict
  691.        try:
  692.            return d[k] == v
  693.        except:
  694.            return 0
  695.  
  696.    _member = member
  697.  
  698.    def delete_arc(self, k, v):
  699.        if self.dict[k] == v:
  700.           del self.dict[k]
  701.        else:
  702.           raise KeyError, "pair not in table"
  703.  
  704.    def has_key(self, k):
  705.        return self.dict.has_key(k)
  706.  
  707.    def neighbors(self, k):
  708.        try:
  709.           return [ self.dict[k] ]
  710.        except: return []
  711.  
  712.    def reachable(self, k):
  713.        result = {}
  714.        d = self.dict
  715.        try:
  716.            while 1:
  717.               next = d[k]
  718.               if result.has_key(next): break
  719.               result[next] = 1
  720.               k = next
  721.        except KeyError:
  722.            pass
  723.        return kjSet(result.keys())
  724.        
  725.    def __invert__(self):
  726.        result = kjDict()
  727.        dr = result.dict
  728.        drtest = dr.has_key
  729.        ds = self.dict
  730.        for (a,b) in ds.items():
  731.            if drtest(b):
  732.               result.dirty=1
  733.            dr[b]=a
  734.        result.dirty = self.dirty or result.dirty
  735.        return result
  736.        
  737.    def __nonzero__(self):
  738.        if self.dict: return 1
  739.        return 0
  740.        
  741.    def subset(s, o):
  742.        oc = o.__class__
  743.        sdict = s.dict
  744.        if oc is kjDict or oc is kjSet:
  745.           odict = o.dict
  746.           otest = odict.has_key
  747.           for k in sdict.keys():
  748.               v = sdict[k]
  749.               if otest(k):
  750.                  if odict[k]!=v:
  751.                     return 0
  752.               else:
  753.                  return 0
  754.        elif oc is kjGraph:
  755.           oktl = o.key_to_list
  756.           otest = oktl.has_key
  757.           for k in sdict.keys():
  758.               v = sdict[k]
  759.               if otest(k):
  760.                  if v not in oktl[k]:
  761.                     return 0
  762.               else:
  763.                  return 0
  764.        else:
  765.           raise ValueError, "kjDict subset test only for kjTable"
  766.        return 1
  767.        
  768.    def add(s, k, v):
  769.        if s.hashed is not None:
  770.           raise ValueError, "table has been hashed, immutable"
  771.        sdict = s.dict
  772.        if sdict.has_key(k):
  773.           if sdict[k]!=v:
  774.              s.dirty = 1
  775.        sdict[k] = v
  776.  
  777. class kjSet(kjDict):
  778.  
  779.    def __init__(self, *args):
  780.        #print "kjSet.__init__", args
  781.        # usual cases first
  782.        dict = self.dict = {}
  783.        self.hashed = None
  784.        self.dirty = 0
  785.        largs = len(args)
  786.        if largs<1: return
  787.        if largs>1:
  788.           raise ValueError, "at most one argument supported"
  789.        from types import IntType, TupleType, ListType
  790.        arg0 = args[0]
  791.        targ0 = type(arg0)
  792.        if targ0 is IntType: return
  793.        if targ0 is TupleType or targ0 is ListType:
  794.           for x in arg0:
  795.               dict[x] = x
  796.           return
  797.        argc = arg0.__class__
  798.        if argc is kjDict or argc is kjSet:
  799.           stuff = arg0.dict.keys()
  800.        elif argc is kjGraph:
  801.           stuff = arg0.key_to_list.keys()
  802.        else:
  803.           raise ValueError, "kjSet from kjTable, int, list, tuple only"
  804.        for x in stuff:
  805.           dict[x] = x
  806.            
  807.    def __add__(s,o):
  808.        oc = o.__class__
  809.        if oc is kjSet:
  810.           result = kjSet()
  811.           result.dirty = s.dirty or o.dirty
  812.           rdict = result.dict
  813.           for x in s.dict.keys():
  814.               rdict[x]=x
  815.           for x in o.dict.keys():
  816.               rdict[x]=x
  817.           return result
  818.        elif oc is kjDict:
  819.           return kjDict.__add__(o,s)
  820.        elif oc is kjGraph:
  821.           return kjGraph.__add__(o,s)
  822.           
  823.    __or__ = __add__
  824.    
  825.    def __sub__(s,o):
  826.        if o.__class__ is kjSet:
  827.           result = kjSet()
  828.           result.dirty = s.dirty or o.dirty
  829.           rdict = result.dict
  830.           otest = o.dict.has_key
  831.           for x in s.dict.keys():
  832.               if not otest(x):
  833.                  rdict[x] = x
  834.           return result
  835.        else:
  836.           return kjDict.__sub__(s,o)
  837.           
  838.    def __and__(s,o):
  839.        oc = o.__class__
  840.        if oc is kjSet or oc is kjDict:
  841.           result = kjSet()
  842.           result.dirty = s.dirty or o.dirty
  843.           rdict = result.dict
  844.           odict = o.dict
  845.           otest = odict.has_key
  846.           for x in s.dict.keys():
  847.               if otest(x) and odict[x]==x:
  848.                  rdict[x] = x
  849.           return result
  850.        elif oc is kjGraph:
  851.           return kjGraph.__and__(o,s)
  852.        raise ValueError, "kjSet only intersects with kjTable"
  853.  
  854.    # illegal methods
  855.    values = keys = remap = None
  856.  
  857.    def __repr__(self):
  858.        return "kjSet(%s)" % self.items()
  859.              
  860.    def _setelts(self, items):
  861.        #print "kjSet.setelts", items
  862.        try:
  863.            items = items._pairs()
  864.        except:
  865.            items = list(items)
  866.            for i in xrange(len(items)):
  867.                items[i] = (items[i], items[i])
  868.            self._setitems(items)
  869.        else:
  870.            items = list(items)
  871.            for i in xrange(len(items)):
  872.                items[i] = (items[i][0], items[i][0])
  873.        self._setitems(items)
  874.        # hack!
  875.        #D = self.dict
  876.        #for x in D.keys():
  877.        #    D[x] = x
  878.  
  879.    def _pairs(self, justtot=0):
  880.        if justtot: return kjDict._pairs(self, justtot=1)
  881.        pairs = kjDict.keys(self)
  882.        for i in xrange(len(pairs)):
  883.            pairs[i] = (pairs[i], pairs[i])
  884.        return pairs
  885.  
  886.    member = kjDict.has_key
  887.  
  888.    items = kjDict.keys
  889.  
  890.    #def neighbors(self, x):
  891.    #    raise ValueError, "operation on kjSet undefined"
  892.  
  893.    #reachable = neighbors
  894.  
  895.    def __getitem__(self, item):
  896.        test = self.dict.has_key(item)
  897.        if test: return 1
  898.        raise KeyError, "item not in set"
  899.        
  900.    def __setitem__(self, item, ignore):
  901.        d = self.dict
  902.        if self.hashed:
  903.           raise ValueError, "table hashed, immutable"
  904.        d[item] = item
  905.        
  906.    def add(self, elt):
  907.        if self.hashed:
  908.           raise ValueError, "table hashed, immutable"
  909.        self.dict[elt] = elt
  910.        
  911.    def __mul__(s,o):
  912.        oc = o.__class__
  913.        if oc is kjSet:
  914.           return s.__and__(o)
  915.        else:
  916.           return kjDict.__mul__(s, o)
  917.  
  918. def more_general(t1, t2):
  919.     try:
  920.         raise t1
  921.     except kjSet:
  922.         try:
  923.            raise t2
  924.         except (kjGraph, kjDict, kjSet):
  925.            return t2.__class__
  926.     except kjDict:
  927.         try:
  928.            raise t2
  929.         except kjSet:
  930.            return t1.__class__
  931.         except (kjDict, kjGraph):
  932.            return t2.__class__
  933.     except kjGraph:
  934.         return t1.__class__
  935.     except:
  936.         raise ValueError, "cannot coerce, not kjtable"
  937.  
  938. def less_general(t1,t2):
  939.     try: 
  940.         raise t1
  941.     except kjSet:
  942.         return t1.__class__
  943.     except kjDict:
  944.         try:
  945.            raise t2
  946.         except kjSet:
  947.            return t2.__class__
  948.         except (kjDict, kjGraph):
  949.            return t1.__class__
  950.     except kjGraph:
  951.         return t2.__class__
  952.     except:
  953.         raise ValueError, "cannot coerce, not kjtable"
  954.  
  955. def kjUndump(t1, t2):
  956.     result = kjDict()
  957.     rdict = result.dict
  958.     lt1 = len(t1)
  959.     if lt1 == 1:
  960.        rdict[t1[0]] = t2
  961.     else:
  962.        # tightly bound to implementation
  963.        for i in xrange(lt1):
  964.            rdict[t1[i]] = t2[i]
  965.     return result
  966.     
  967.  
  968. def test():
  969.     global S, D, G
  970.     G = kjGraph()
  971.     r3 = range(3)
  972.     r = map(None, r3, r3)
  973.     for i in range(3):
  974.         G[i] = i+1
  975.     D = kjDict(G)
  976.     D[9]=0
  977.     G[0]=10
  978.     S = kjSet(G)
  979.     S[-1] = 5
  980.     print "%s.remap(%s) = %s" % (D, G, D.remap(G))
  981.     print "init test"
  982.     for X in (S, D, G, r, tuple(r), 1):
  983.         print "ARG", X
  984.         for C in (kjGraph, kjSet, kjDict):
  985.             print "AS", C
  986.             T = C(X)
  987.             T2 = C()
  988.             print X, T, T2
  989.     ALL = (S, D, G)
  990.     for X in ALL:
  991.         print "X=", X
  992.         print "key", X.choose_key()
  993.         print "len", len(X)
  994.         print "items", X.items()
  995.         print X, "Clean before", X.Clean()
  996.         del X[2]
  997.         print X, "Clean after", X.Clean()
  998.         if not X.subset(X):
  999.            raise "trivial subset fails", X
  1000.         if not X==X:
  1001.            raise "trivial cmp fails", X
  1002.         if not X:
  1003.            raise "nonzero fails", X
  1004.         if X is S:
  1005.            if not S.member(0):
  1006.               raise "huh 1?"
  1007.            if S.member(123):
  1008.               raise "huh 2?", S
  1009.            S.add(999)
  1010.            del S[1]
  1011.            if not S.has_key(999):
  1012.               raise "huh 3?", S
  1013.         else:
  1014.            print "values", X.values()
  1015.            print "keys", X.keys()
  1016.            print X, "inverted", ~X
  1017.            if not X.member(0,1):
  1018.               raise "member test fails (0,1)", X
  1019.            print "adding to", X
  1020.            X.add(999,888)
  1021.            print "added", X
  1022.            X.delete_arc(999,888)
  1023.            print "deleted", X
  1024.            if X.member(999,888):
  1025.               raise "member test fails (999,888)", X
  1026.            if X.has_key(999):
  1027.               raise "has_key fails 999", X
  1028.            if not X.has_key(0):
  1029.               raise "has_key fails 0", X
  1030.         for Y in ALL:
  1031.             print "Y", Y
  1032.             if (X!=S and Y!=S):
  1033.                print "diff", X, Y
  1034.                print "%s-%s=%s" % (X,Y,X-Y)
  1035.             elif X==S:
  1036.                D = kjSet(Y)
  1037.                print "diff", X, D
  1038.                print "%s-%s=%s" % (X,D,X-D)
  1039.             print "%s+%s=%s" % (X,Y,X+Y)
  1040.             print "%s&%s=%s" % (X,Y,X&Y)
  1041.             print "%s*%s=%s" % (X,Y,X*Y)
  1042.             x,y = cmp(X,Y), cmp(Y,X)
  1043.             if x!=-y: raise "bad cmp!", (X, Y)
  1044.             print "cmp(X,Y), -cmp(Y,X)", x,-y
  1045.             print "X.subset(Y)", X.subset(Y)
  1046.             
  1047.  
  1048.  
  1049.            
  1050.